package com.ebupt.migu.music.autoTri.util;

import com.alibaba.fastjson.JSONObject;
import com.ebupt.migu.music.api.entity.PushContentObject;
import com.ebupt.migu.music.api.entity.PushPolicyObject;
import com.ebupt.migu.music.api.entity.PushRequestBody;
import com.ebupt.migu.music.autoTri.Entity.ResultMsg;
import com.ebupt.migu.music.autoTri.Mapper.AutoTouchOffMapper;
import com.ebupt.migu.music.common.api.ApiResponse;
import com.ebupt.migu.music.common.util.DateUtil;
import com.ebupt.migu.music.common.util.HttpUtils;
import com.ebupt.migu.music.common.util.JsonUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.HtmlUtils;
import org.apache.commons.codec.binary.Base64;

import javax.annotation.PostConstruct;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;

/**
 * @author xuhongsong
 * @version 1.0
 * @date 2020/8/5 16:07
 */
@Component
@Slf4j
public class SendService {

    @Value("${push.ip}")
    private String PUSH_IP;
    @Value("${push.app_id}")
    private String PUSH_APP_ID;
    @Value("${push.app_key}")
    private String PUSH_APP_KEY;
    @Value("${push.app_secret}")
    private String PUSH_APP_SECRET;
    @Value("${push.push_url}")
    private String PUSH_URL;
    @Value("${path.image_server_path}")
    private String imageServerPath;

    private static SendService sendService;
    @Autowired
    private AutoTouchOffMapper autoTouchOffMapper;

    @PostConstruct
    public void init() {
        sendService = this;
        sendService.autoTouchOffMapper = this.autoTouchOffMapper;
    }

    /**
     * 消息通知策略参数
     */
    @Value("${msg.appid}")
    private String MSG_APP_ID;
    @Value("${msg.account}")
    private String MSG_ACCOUNT;
    @Value("${msg.password}")
    private String MSG_PASSWORD;
    @Value("${msg.userip}")
    private String MSG_USER_IP;
    @Value("${msg.appkey}")
    private String MSG_APP_KEY;
    @Value("${msg.channelid}")
    private String MSG_CHANNEL_ID;
    @Value("${msg.url}")
    private String MSG_URL;
    @Value("${msg.cdn_server}")
    private String MSG_CDN_SERVER;

    public ResultMsg send(Map<String, Object> param) {
        ResultMsg resultMsg = null;
        switch (param.get("tacMode").toString()) {
            case "2":
                // 调用push
                resultMsg = sendPush(param);
                break;
            case "4":
                // 调用Msg
                resultMsg = sendMsg(param);
                break;
        }
        return resultMsg;
    }

    /**
     * push策略
     *
     * @param param
     * @return
     */
    private ResultMsg sendPush(Map<String, Object> param) {
        ResultMsg res = new ResultMsg();
        //构造PushContentObject实例
        PushContentObject pushContentObject = new PushContentObject();
        pushContent(param, pushContentObject);
        //push策略uuid数组
        List<String> uids = new ArrayList<>();
        //push策略设备数组
        List<String> dids = new ArrayList<>();
        //用户标识
        String userId = param.get("userId").toString();
        uids.add(userId);
        String taskId = param.get("tacId").toString() + DateUtil.date2str(new Date(), DateUtil.DATE_FORMAT_YYYYMMDD);
        String title = param.get("title").toString();
        //  内容
        String content = param.get("content").toString();
        pushContentObject.setPushContent(content);
        pushContentObject.setPushTitle(title);
        log.info("替换后的pushContentObject:{}", pushContentObject.toString());
        //push推送
        ApiResponse apiResponse = pushToServer(taskId, pushContentObject, uids, dids, title);
        if (apiResponse != null && apiResponse.getCode().equals(Sysconst.SUCCESS)) {
            res.setMsg("push推送成功");
        } else {
            res.setCode(Sysconst.FAIL_CODE);
            res.setMsg("push推送失败");
        }
        res.setCid(apiResponse.getData().toString());
        res.setUid(userId);
        return res;
    }

    /**
     * 消息通知
     *
     * @param param
     * @return
     */
    private ResultMsg sendMsg(Map<String, Object> param) {
        ResultMsg res = new ResultMsg();
        //用户标识
        String userId = param.get("userId").toString();
        res.setUid(userId);
        String taskId = param.get("tacId").toString() + DateUtil.date2str(new Date(), DateUtil.DATE_FORMAT_YYYYMMDD);
        //生成消息通知body
        JSONObject bodys = body(param);
        //构造xml结构
        StringBuffer cons = new StringBuffer();
        cons.append("<sendMessage>");
        cons.append("<sendMessageReq>");
        cons.append("<userID>-1</userID>");
        cons.append("<targetIDList>");
        cons.append("<string>");
        cons.append(userId);
        cons.append("</string>");
        cons.append("</targetIDList>");
        cons.append("<messageInfo>");
        cons.append("<type>0</type>");
        cons.append("<action>11</action>");
        cons.append("<body>");
        cons.append("<![CDATA[");
        cons.append("<message>");
        cons.append(bodys);
        cons.append("</message>");
        cons.append("]]>");
        cons.append("</body>");
        cons.append("<ext>");
        cons.append("<item>");
        cons.append("<key>taskId</key>");
        cons.append("<value>");
        cons.append(taskId);
        cons.append("</value>");
        cons.append("</item>");
        cons.append("</ext>");
        cons.append("</messageInfo>");
        cons.append("</sendMessageReq>");
        cons.append("</sendMessage>");
        //发送消息通知
        String response = doPost(MSG_URL, cons.toString());
        log.info("消息通知返回结果：{}", response);
        //截取响应字串信息
        String code = response.substring(response.indexOf("<desc>") + 6, response.indexOf("</desc>"));
        if ("success".equals(code)) {
            param.put("successNum", 1);
            res.setMsg("消息通知推送成功");
        } else {
            res.setCode(Sysconst.FAIL_CODE);
            res.setMsg("消息通知推送失败");
        }
        return res;
    }

    /**
     * 构造body
     *
     * @param map
     * @return
     */
    private JSONObject body(Map<String, Object> map) {
        //  用这个取发送者
        String msgNickName = map.get("title").toString();
        Map<String, Object> info = sendService.autoTouchOffMapper.queryMsgTypeInfo(msgNickName);
        JSONObject body = new JSONObject();
        body.put("text", map.get("content").toString());
        if (null != map.get("picUrl") && !"".equals(map.get("picUrl").toString())) {
            String msg_pic = map.get("picUrl").toString();
            String dir = MSG_CDN_SERVER + msg_pic.substring(msg_pic.lastIndexOf("/") + 1);
            try {
                body.put("pic", URLEncoder.encode(dir, "UTF-8"));
            } catch (UnsupportedEncodingException e) {
                log.error("pic's url encode error:{}", e.getMessage());
            }
        }
        if (null != map.get("linkAddress")) {
            try {
                body.put("link", URLEncoder.encode(map.get("linkAddress").toString(), "UTF-8"));
            } catch (UnsupportedEncodingException e) {
                log.error("msg_link's url encode error:{}", e.getMessage());
            }
        }
        JSONObject sender = new JSONObject();
        String icon = info.get("icon").toString();
        String icon_dir = MSG_CDN_SERVER + icon.substring(icon.lastIndexOf("/") + 1);
        try {
            sender.put("icon", URLEncoder.encode(icon_dir, "UTF-8"));
        } catch (UnsupportedEncodingException e) {
            log.error("icon's url encode error:{}", e.getMessage());
        }
        sender.put("nickName", HtmlUtils.htmlEscape(msgNickName));
        body.put("sender", sender);
        return body;
    }

    /**
     * 构造PushContentObject实例
     *
     * @param strategy
     * @param pushContentObject
     */
    private void pushContent(Map<String, Object> strategy, PushContentObject pushContentObject) {
        // 需要进行HTML解码
        pushContentObject.setPushTitle(HtmlUtils.htmlUnescape(strategy.get("title").toString()));
        pushContentObject.setPushContent(HtmlUtils.htmlUnescape(strategy.get("content").toString()));
        // 默认值是1
        pushContentObject.setPushType(1);
        // 1.前端输入链接以：mgmusic开头的，pushLinkType=100
        // 2.输入链接以http或以https开头的，pushLinkType=9
        if (null != strategy.get("linkAddress")) {
            String pushURL = strategy.get("linkAddress").toString();
            if (pushURL.startsWith("mgmusic")) {
                pushContentObject.setPushLinkType(100);
            } else {
                pushContentObject.setPushLinkType(9);
            }
            pushContentObject.setPushLinkAddress(pushURL);
        }
        if (null != strategy.get("picUrl") && !"".equals(strategy.get("picUrl"))
                && !" ".equals(strategy.get("picUrl"))) {
            String picDir = strategy.get("picUrl").toString();
            picDir = imageServerPath + picDir.substring(picDir.lastIndexOf("/") + 1);
            pushContentObject.setImgUrl(picDir);
        }
        pushContentObject.setPushCreateTime(System.currentTimeMillis());
        log.info("push content object:{}", pushContentObject.toString());
    }


    /**
     * push推送
     *
     * @param taskId
     * @param pushContentObject
     */
    private ApiResponse pushToServer(String taskId, PushContentObject pushContentObject, List<String> uids, List<String> dids, String topic) {
        PushRequestBody body = new PushRequestBody();
        // policy固定值
        PushPolicyObject policy = new PushPolicyObject();
        policy.setBroadcast(false);
        policy.setIsOffline(true);
        policy.setPushNetWorkType(0);
        body.setPolicy(policy);
        body.setAppId(PUSH_APP_ID);
        body.setTaskId(taskId);
        body.setContent(pushContentObject);
        body.setUid(uids);
        body.setDid(dids);
        body.setTopic(HtmlUtils.htmlUnescape(topic));
        body.setPushChannel("1");
        body.setIsSync(true);
        // 转Map
        Map<String, Object> params = beanToMap(body);
        // 生成Header
        Map<String, String> header = header(taskId, body);
        // URL
        String url = PUSH_IP + PUSH_URL;
        log.info("服务端URL: {}", url);
        String response = HttpUtils.doPost(url, params, header);
        ApiResponse apiResponse = JsonUtils.json2Obj(response, ApiResponse.class);
        log.info("apiResponse: {}", apiResponse);
        return apiResponse;
    }

    /**
     * JavaBean转换Map
     *
     * @param object
     * @return
     */
    private static Map<String, Object> beanToMap(Object object) {
        Map<String, Object> result = new HashMap<>();
        //获得类的的属性名数组
        Field[] fields = object.getClass().getDeclaredFields();
        try {
            for (Field field : fields) {
                field.setAccessible(true);
                String name = new String(field.getName());
                result.put(name, field.get(object));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 请求签名
     * 使用场景: 接入方发送HTTP请求时需要对请求做签名校验
     * 签名方式:
     * Map<String, Object> headers = new HashMap<>();
     * StringBuilder sb = new StringBuilder("MIGU").append("\n");
     * sb.append(taskId).append("\n");
     * sb.append(pushChannel).append("\n");
     * sb.append(pushContent).append("\n");
     * sb.append(timestamp).append("\n");
     * sb.append(AppSecret).append("\n");
     * String Authorization = AppKey + ":" + MD5Util.MD5(sb.toString());
     * headers.put("Authorization", Authorization);
     * headers.put("timestamp", timestamp);
     * 其中:
     * MIGU:固定值
     * \n:换行转义符
     * taskId: 任务id,唯一标示
     * pushChannel: 推送渠道1-个推 当前只支持个推渠道
     * pushContent:传输到推送系统的content.getPushContent() 详见Content Object
     * timestamp:系统时间戳
     * AppKey :推送系统分配的appKey
     * AppSecret:推送系统分配的密钥
     * WARN: 接入方系统时间戳跟推送系统需要保持基本一致，防止重放攻击
     * 推送说明:
     * broadcast为true时表示针对app做推送 此时platform生效，表示对手机系统app推送
     * 如果推送列表 broadcast必为false, uid或者cid列表长度大于1。
     * 当前只支持个推推送，所有pushChannel固定为1。
     * 当uid或者cid长度为1时表示对单个用户进行推送。
     * md5值为小写md5
     */
    private Map<String, String> header(String taskId, PushRequestBody body) {
        Map<String, String> map = new HashMap<>();

        String timestamp = String.valueOf(System.currentTimeMillis());
        StringBuilder sb = new StringBuilder("MIGU").append("\n");
        sb.append(taskId).append("\n");
        sb.append(body.getPushChannel()).append("\n");
        sb.append(body.getContent().getPushContent()).append("\n");
        sb.append(timestamp).append("\n");
        sb.append(PUSH_APP_SECRET).append("\n");
        String Authorization = PUSH_APP_KEY + ":" + DigestUtils.md5Hex(sb.toString());
        map.put("Content-Type", "application/json; charset=utf-8");
        map.put("Authorization", Authorization);
        map.put("timestamp", timestamp);
        return map;
    }


    /**
     * HTTP请求
     *
     * @param url
     * @param request
     * @return
     */
    private String doPost(String url, String request) {
        log.info("访问参数:{}", request);
        // 设置代理服务器
        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
        RestTemplate restTemplate = new RestTemplate(factory);
        // 设置编码集
        restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_XML);
        headers.set("Authorization", authorization());
        HttpEntity<String> entity = new HttpEntity<>(request, headers);
        ResponseEntity response = restTemplate.postForEntity(url, entity, String.class);
        log.info("response:{}", response);
        if (null != response) {
            return response.toString();
        } else {
            return "请求异常！";
        }
    }

    /**
     * header authorization -用户验证信息
     *
     * @return
     */
    private String authorization() {
        // part1：   密码
        String password = MSG_PASSWORD;
        // part2：   6位随机码
        String nonce = getRandom();
        // part3：   created时间戳
        String created = DateUtil.date2str(new Date(), DateUtil.TRI_TIME);
        // 加密
        String password_base_64 = base_64_encode(sha_256(password + nonce + created));
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append("Basicappid=\"");
        stringBuffer.append(MSG_APP_ID);
        stringBuffer.append("\",account=\"");
        stringBuffer.append(MSG_ACCOUNT);
        stringBuffer.append("\",password=\"");
        stringBuffer.append(password_base_64);
        stringBuffer.append("\",nonce=\"");
        stringBuffer.append(nonce);
        stringBuffer.append("\",created=\"");
        stringBuffer.append(created);
        stringBuffer.append("\",userip=\"");
        stringBuffer.append(MSG_USER_IP);
        stringBuffer.append("\",appkey=\"");
        stringBuffer.append(MSG_APP_KEY);
        stringBuffer.append("\",channelid=\"");
        stringBuffer.append(MSG_CHANNEL_ID);
        stringBuffer.append("\"");
        String auth = stringBuffer.toString();
        log.info("header authorization -用户验证信息:{}", auth);
        return auth;
    }

    /**
     * 获取6位长度随机数
     *
     * @return
     */
    public static String getRandom() {
        StringBuffer val = new StringBuffer();
        Random random = new Random();
        for (int i = 0; i < 6; i++) {
            val.append((random.nextInt(10)));
        }
        return val.toString();
    }

    /**
     * base64进行加密
     *
     * @return
     */
    private static String base_64_encode(byte[] bytes) {
        return Base64.encodeBase64String(bytes);
    }

    /**
     * sha-256加密算法
     *
     * @param str
     * @return
     */
    public static byte[] sha_256(String str) {
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
            messageDigest.update(str.getBytes());
            return messageDigest.digest();
        } catch (NoSuchAlgorithmException e) {
            log.error("SHA-256加密出错:{}", e.getMessage());
        }
        log.error("sha-256加密失败");
        return null;
    }
}
